package com.leaf.u.basehttp.download;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import com.activeandroid.query.Select;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.Queue;
import java.util.TimerTask;

public class TaskHandler<T extends DownloadModel> implements Runnable, Comparable<Object> {
    private IHttp ihttp;// HttpRequest
    private DownloadResponseListener<T> mListener;
    private DownloadManager<T> mDownloadManager;
    private T model;
    // private Class<T> modelClass;
    private long lastLoadingTime = 0;
    private boolean callLoading = true;
    private DownloadRecord temp;
    private Queue<DownloadRecord> speedTemp = new LinkedList<DownloadRecord>();
    private TimerTask timertask;
    // private IDownloadDataDao<?, Tag> data;
    Looper mainLooper = Looper.getMainLooper();
    Handler handler = new Handler(mainLooper) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            notifyListener();
        }
    };

    class MyTimerTask extends TimerTask {
        @Override
        public void run() {
//            callLoading = true;
//            if (callLoading) {
//                callLoading = false;
//            }
            if (model.getStatus() == State.LOADING) {
                notifyListener(State.LOADING);
            }
        }
    }

    public String getUrl() {
        return this.model.getUrl();
    }

    public void setListener(DownloadResponseListener<T> l) {
        mListener = l;
        // model.setStatus(State.);
    }

    public TaskHandler(IHttp ihttp, DownloadManager<T> dm, T model) {

        if (model == null) {
            throw new IllegalArgumentException("model must not be null");
        }
        if (model.getId() != null) {
            this.model = model;
        } else {
            Select select = new Select();
            T localModel = select.from(model.getClass()).where("url=?", model.getUrl()).executeSingle();
            if (localModel != null) {
                this.model = localModel;
            } else {
                this.model = model;
            }
        }
        if (model.getStatus() == State.SUCCESS || model.getStatus() == State.FAILURE) {
        } else {
            model.setStatus(State.PAUSE);
        }
        this.ihttp = ihttp;
        this.mDownloadManager = dm;
        // this.data = data;
    }

    synchronized void notifyListener(State state) {
        switch (state) {
            case LOADING:
                mDownloadManager.notifyObservers(DownloadManager.TaskState.LOAD, this);
                break;
            case SUCCESS:
                model.setStatus(state);
                model.save();
                speedTemp.clear();
                mDownloadManager.notifyObservers(DownloadManager.TaskState.FINISH, this);
                break;
            case DELETED:
                model.setStatus(state);
                break;
            case STANDBY:
                model.setStatus(State.STANDBY);
                mDownloadManager.notifyObservers(DownloadManager.TaskState.STANDBY, this);
                break;
            case FAILURE:
                model.setStatus(state);
                model.save();
                speedTemp.clear();
                mDownloadManager.notifyObservers(DownloadManager.TaskState.FAILURE, this);
                break;
            case PAUSE:
                model.setStatus(state);
                mDownloadManager.notifyObservers(DownloadManager.TaskState.PAUSE, this);
            default:
                model.setStatus(state);
                model.save();
                speedTemp.clear();
                break;
        }
        handler.sendEmptyMessage(0);
    }

    void startLoop() {
        if (timertask != null) {
            timertask.cancel();
        }
        timertask = new MyTimerTask();
        mDownloadManager.getTimer().schedule(timertask, 800, 800);
    }

    void endLoop() {
        if (timertask != null) {
            timertask.cancel();
        }
        //会清空定时器中任务为CANCEL的任务
        mDownloadManager.getTimer().purge();
    }

    void notifyListener() {
        if (mListener == null || model == null) {
            return;
        }
        if (mListener.updateURL() == null || !mListener.updateURL().equals(model.getUrl())) {
            return;
        }
        // TODO Change Status
        State status = model.getStatus();
        switch (status) {
            case PAUSE:
                mListener.onPause(model);
                break;
            case FAILURE:
                mListener.onFailure(model);
                break;
            case SUCCESS:
                model.setSpeed(calcSpeed());
                mListener.onProgress(model);
                mListener.onSuccess(model);
                break;
            case LOADING:
                model.setSpeed(calcSpeed());
                mListener.onProgress(model);
                break;
            case START:
                mListener.onStart(model);
                break;
            case DELETED:
                mListener.onDelete(model);
                break;
            case ENQUEUE:
                mListener.enQueue(model);
                break;
            case SUSPENDED:
                mListener.suspended(model);
                break;
            default:
                break;
        }
    }


    public T get() {
        return model;
    }

    public void delete() {
        if (model.getStatus() == State.DELETED) {
            return;
        }
        //如果正在下载切换到run方法中删除任务
        if (model.getStatus() != State.LOADING) {
            mDownloadManager.delTask(this);
        }
        model.setStatus(State.DELETED);
        model.delete();
    }

    /**
     * 删除数据库和文件
     */
    public void deleteRes() {
        File file = new File(model.getAbsolutePath());
        if (file.exists()) {
            file.delete();
        }
        delete();
    }

    public synchronized void start() {
        if (model.getStatus() == State.SUSPENDED) {
            throw new IllegalStateException("can not handle the suspended task");
        }
        if (model.getStatus() == State.DELETED || model.getStatus() == State.ENQUEUE) {
            return;
        }
        mDownloadManager.addTask(model);
    }

    public synchronized void stop() {
        if (model.getStatus() == State.SUSPENDED) {
            throw new IllegalStateException("can not handle the suspended task");
        }
        if (model.getStatus() == State.DELETED) {
            return;
        }
        if (model.getStatus() == State.ENQUEUE) {
            mDownloadManager.remove(this);
            model.setStatus(State.PAUSE);
            notifyListener(State.PAUSE);
            return;
        }
        if (model.getStatus() == State.START || model.getStatus() == State.STANDBY || model.getStatus() == State.LOADING) {
            model.setStatus(State.SUSPENDED);
            notifyListener(State.SUSPENDED);
        }
    }

    class DownloadRecord {
        private long timeRecord;
        private long lengthRecord;
    }

    private float calcSpeed() {
        long curTime = System.currentTimeMillis();
        long diffTime = -1;
        long curLength = model.getCurLength();
        long diffLength = 0;
        while (speedTemp.size() < 6) {
            DownloadRecord dr = new DownloadRecord();
            dr.lengthRecord = model.getCurLength();
            dr.timeRecord = curTime;
            speedTemp.add(dr);
        }
        DownloadRecord record = speedTemp.remove();
        temp = record;
        diffTime = curTime - record.timeRecord + 1;
        diffLength = curLength - record.lengthRecord;
        temp.lengthRecord = curLength;
        temp.timeRecord = curTime;
        speedTemp.add(temp);
        float speed = 1f * diffLength / (1f * diffTime / 1000);
        return speed;
    }

    @Override
    public void run() {
        RandomAccessFile fos = null;
        InputStream is = null;
        int len = 0;
        long curLen = 0;
        File file = null;
        byte[] buf = new byte[4096];
        try {
            if (model.getStatus() != State.ENQUEUE && model.getStatus() == State.SUSPENDED) {
                notifyListener(State.PAUSE);
                return;
            }
            notifyListener(State.START);

            file = new File(model.getAbsolutePath());
            if (!file.exists()) {
                File current = file.getParentFile();
                if (current.exists() || current.mkdirs()) {
                    file.createNewFile();
                }
            }
            curLen = file.length();
            Request.Builder builder = new Request.Builder();
            if (curLen > 0) {
                builder.addHeader("RANGE", "bytes=" + curLen + "-");
            }
            builder.setUrl(model.getUrl());

            Request request = builder.build();
            //网络请求（耗时）
            Response response = ihttp.get(request);
            is = response.inputStream();

            //contentLength为本次请求所接收的内容的总长度
            //（不一定为文件的总长度，断点续传的contentLength小于文件长度）
            if (file.getUsableSpace() <= response.contentLength()) {
                model.setError(Error.OutOfDiskSpace);
                notifyListener(State.FAILURE);
                return;
            }
            if (response.code() > 299 || response.code() < 200) {
                model.setError(Error.NetWorkError);
                notifyListener(State.FAILURE);
                return;
            }
            if (is == null) {
                model.setError(Error.NetWorkError);
                notifyListener(State.FAILURE);
                return;
            }
            // timer./
            //
            fos = new RandomAccessFile(file, "rw");
            fos.seek(curLen);
            if (model.getLength() != curLen + response.contentLength()) {
                model.setLength(curLen + response.contentLength());
            }

            if (model.getStatus() == State.START) {
                model.setStatus(State.LOADING);
            }
            // timer = new
            startLoop();
            //网络请求（耗时）
            while (model.getStatus() == State.LOADING && (len = is.read(buf)) != -1) {
                fos.write(buf, 0, len);
                curLen += len;
                model.setCurLenght(curLen);
            }
            endLoop();
            if (model.getStatus() == State.DELETED) {
                mDownloadManager.delTask(this);
                notifyListener(State.DELETED);
            } else if (curLen == model.getLength()) {
                notifyListener(State.SUCCESS);
            } else if (curLen < model.getLength() && model.getStatus() == State.SUSPENDED) {
                notifyListener(State.PAUSE);
            } else if (model.getStatus() == State.STANDBY) {
                notifyListener(State.STANDBY);
            } else {
                model.setError(Error.Other);
                notifyListener(State.FAILURE);
            }
        } catch (SocketTimeoutException e) {
            e.printStackTrace();
            model.setError(Error.NetWorkError);
            notifyListener(State.FAILURE);
        } catch (UnknownHostException e1) {
            e1.printStackTrace();
            model.setError(Error.NetWorkError);
            notifyListener(State.FAILURE);
        } catch (ConnectException e) {
            e.printStackTrace();
            model.setError(Error.NetWorkError);
            notifyListener(State.FAILURE);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            model.setError(Error.NetWorkError);
            notifyListener(State.FAILURE);
        } catch (IOException e) {
            e.printStackTrace();
            if (model.getStatus() == State.DELETED) {
                mDownloadManager.delTask(this);
                notifyListener(State.DELETED);
            } else if (file != null && model != null && file.getUsableSpace() <= model.getLength()) {
                long length = file.getUsableSpace();
                model.setError(Error.OutOfDiskSpace);
                notifyListener(State.FAILURE);
            } else {
                model.setError(Error.Other);
                notifyListener(State.FAILURE);
            }
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            endLoop();
        }
    }

    @Override
    public int compareTo(Object another) {
        return 0;
    }
}